
类模板偏特化可用于为特定模板参数提供类模板的特化实现，就像我们为函数模板使用重载一样。与重载函数模板一样，可以根据模板参数的属性区分偏特化。考虑使用键值类型作为模板参数的泛型Dictionary类模板。只要键类型只提供相等操作符，就可以实现一个简单(但效率低)的Dictionary:

\begin{lstlisting}[style=styleCXX]
template<typename Key, typename Value>
class Dictionary
{
	private:
	vector<pair<Key const, Value>> data;
	public:
	// subscripted access to the data:
	value& operator[](Key const& key)
	{
		// search for the element with this key:
		for (auto& element : data) {
			if (element.first == key) {
				return element.second;
			}
		}
		// there is no element with this key; add one
		data.push_back(pair<Key const, Value>(key, Value()));
		return data.back().second;
	}
	...
};
\end{lstlisting}

若键类型支持小于操作符，则可以基于标准库的map容器提供更有效的实现。类似地，若键类型支持哈希操作，可以使用标准库的unordered\_map。

\subsubsubsection{20.4.1\hspace{0.2cm}启用/禁用类模板}

启用/禁用类模板的不同实现的方法是使用启用/禁用类模板的偏特化。要在类模板偏特化中使用EnableIf，首先向Dictionary引入一个未命名的默认模板参数:

\begin{lstlisting}[style=styleCXX]
template<typename Key, typename Value, typename = void>
class Dictionary
{
	... // vector implementation as above
};
\end{lstlisting}

新的模板参数作为EnableIf的锚，可以嵌入到Dictionary的map版本的偏特化模板参数列表中:

\begin{lstlisting}[style=styleCXX]
template<typename Key, typename Value>
class Dictionary<Key, Value,
				EnableIf<HasLess<Key>>>
{
	private:
	map<Key, Value> data;
	public:
	value& operator[](Key const& key) {
		return data[key];
	}
	...
};
\end{lstlisting}

与重载函数模板不同，这里不需要在主模板上禁用条件，因为偏特化优先于主模板。当使用哈希操作添加另一个键实现时，需要确保偏特化条件互斥:

\begin{lstlisting}[style=styleCXX]
template<typename Key, typename Value, typename = void>
class Dictionary
{
	... // vector implementation as above
};

template<typename Key, typename Value>
class Dictionary<Key, Value,
				EnableIf<HasLess<Key> && !HasHash<Key>>> 
{
	... // map implementation as above
};

template<typename Key, typename Value>
class Dictionary<Key, Value,
EnableIf<HasHash<Key>>>
{
	private:
	unordered_map<Key, Value> data;
	public:
	value& operator[](Key const& key) {
		return data[key];
	}
	...
};
\end{lstlisting}

\subsubsubsection{20.4.2\hspace{0.2cm}类模板的标签调度}

标签调度也可以用于在类模板偏特化之间进行选择。我们定义了一个函数对象类型Advance<Iterator>，类似于前面章节中使用的advanceIter()算法，将迭代器向前推进若干步。我们提供了通用实现(用于输入迭代器)，以及双向和随机访问迭代器的特化实现，依赖于一个辅助特征BestMatchInSet(如下所述)来为迭代器的类别标签选择最佳匹配:

\begin{lstlisting}[style=styleCXX]
// primary template (intentionally undefined):
template<typename Iterator,
		typename Tag =
			BestMatchInSet<
				typename std::iterator_traits<Iterator>
							::iterator_category,
				std::input_iterator_tag,
				std::bidirectional_iterator_tag,
				std::random_access_iterator_tag>>
class Advance;

// general, linear-time implementation for input iterators:
template<typename Iterator>
class Advance<Iterator, std::input_iterator_tag>
{
	public:
	using DifferenceType =
	typename std::iterator_traits<Iterator>::difference_type;
	void operator() (Iterator& x, DifferenceType n) const
	{
		while (n > 0) {
			++x;
			--n;
		}
	}
};

// bidirectional, linear-time algorithm for bidirectional iterators:
template<typename Iterator>
class Advance<Iterator, std::bidirectional_iterator_tag>
{
	public:
	using DifferenceType =
		typename std::iterator_traits<Iterator>::difference_type;
		
	void operator() (Iterator& x, DifferenceType n) const
	{
		if (n > 0) {
			while (n > 0) {
				++x;
				--n;
			}
		} else {
			while (n < 0) {
				--x;
				++n;
			}
		}
	}
};

// bidirectional, constant-time algorithm for random access iterators:
template<typename Iterator>
class Advance<Iterator, std::random_access_iterator_tag>
{
	public:
	using DifferenceType =
		typename std::iterator_traits<Iterator>::difference_type;
		
	void operator() (Iterator& x, DifferenceType n) const
	{
		x += n;
	}
}
\end{lstlisting}

表达式与函数模板的标签调度非常相似，问题在于编写特征BestMatchInSet，该特征要确定给定迭代器的(输入、双向和随机访问迭代器标签)最匹配的标签。这个特征说明，给定迭代器的category标签的值，将选择下列哪个重载，并报告其参数类型:

\begin{lstlisting}[style=styleCXX]
void f(std::input_iterator_tag);
void f(std::bidirectional_iterator_tag);
void f(std::random_access_iterator_tag);
\end{lstlisting}

模拟重载解析最简单的方法是使用重载解析:

\begin{lstlisting}[style=styleCXX]
// construct a set of match() overloads for the types in Types...:
template<typename... Types>
struct MatchOverloads;

// basis case: nothing matched:
template<>
struct MatchOverloads<> {
	static void match(...);
};

// recursive case: introduce a new match() overload:
template<typename T1, typename... Rest>
struct MatchOverloads<T1, Rest...> : public MatchOverloads<Rest...> {
	static T1 match(T1); // introduce overload for T1
	using MatchOverloads<Rest...>::match; // collect overloads from bases
};

// find the best match for T in Types...:
template<typename T, typename... Types>
struct BestMatchInSetT {
	using Type = decltype(MatchOverloads<Types...>::match(declval<T>()));
};

template<typename T, typename... Types>
using BestMatchInSet = typename BestMatchInSetT<T, Types...>::Type;
\end{lstlisting}

MatchOverloads模板使用递归继承来为Types输入集中的每个类型声明一个match()函数。递归match重载偏特化的每次实例化，都会为列表中的下一个类型引入一个新的match()函数。然后，使用using声明引入在其基类中定义的match()函数，该函数处理列表中的其余类型。当处理递归时，结果是一组对应于给定类型的match()重载，每个重载返回其参数类型。BestMatchInSetT模板然后将一个T对象传递给这组重载match()函数，并生成所选(最佳)match()函数的返回类型。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}C++17中，可以在基类列表和using声明中使用包扩展来消除递归(第4.4.5节)。将在第26.4节演示这种技术。
\end{tcolorbox}

如果两个函数都不匹配，则返回void(使用省略号来捕获参数)表示失败。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}失败的情况下，不提供结果会更好一些，使其成为SFINAE友好特征(参见第19.4.4节)。此外，健壮的实现将返回类型包装在类似Identity的东西中，因为有些类型(如数组和函数类型)可以是参数类型，但不能是返回类型。为了简洁和可读性，省略了这些改进。
\end{tcolorbox}

总而言之，BestMatchInSetT将函数重载结果转换为特征，并使标签调度在类模板偏特化间的选择，变得相对容易。










